#include "image.h"

using std::abs;

Image::Image()
{
	width = 0;
	height = 0;
	widthStep = width * channel;
	channel = C1;
	data = 0;
}

void Image::Release()
{
	if(this->data!=0)
		delete [] this->data;
}

Image::Image(unsigned int _width, unsigned int _height, unsigned int _channel, unsigned char* _data):
	data(_data), width(_width), height(_height)
{
	widthStep = width * _channel;

	switch(_channel)
	{
		case 1:
			channel = C1;
			break;
		case 2:
			channel = C2;
			break;
		case 3:
			channel = C3;
			break;
	}
	
	if (data==0)
		data = new unsigned char[height * width * _channel];
}

Image::Image(const Image &src, I_Rect rect)
{
	int l = std::max(rect.x-rect.width/2, 0);
	int r = std::min(rect.x-rect.width/2 + rect.width, src.width);
	int u = std::max(rect.y-rect.height/2, 0);
	int d = std::min(rect.y-rect.height/2 + rect.height, src.height);
	
	width = std::max(r-l, 0);
	height = std::max(d-u, 0);	
	channel = src.channel;
	int _channel = channel +1;
	widthStep = width * _channel;		
	data = new unsigned char[height * width * _channel];
	
	for(int j = u;j<d;j++)
		for(int i = l;i<r;i++)
		{
			data[(j-u)*widthStep + (i-l)*_channel] = src.data[j*src.widthStep + i * _channel];
			data[(j-u)*widthStep + (i-l)*_channel + 1] = src.data[j*src.widthStep + i * _channel + 1];
			data[(j-u)*widthStep + (i-l)*_channel + 2] = src.data[j*src.widthStep + i * _channel + 2];
		}
}

bool Image::Check(const Image &src, const Image &dst) const
{
	if(src.height!=dst.height)
		return false;
	if(src.width!=dst.width)
		return false;
	if(src.widthStep!=dst.widthStep)
		return false;
	
	return true;
}

void Image::CopyTo(Image &dst, const Image &mask) const
{
	if(mask.channel!=C1)
		Log("Channel of mask image is wrong",L_ERROR);

	int _channel = channel +1;
	

	Image image(width, height, _channel);

	if(!Check(image, mask) && mask.data!=0)
		Log("Wrong size",L_ERROR);
	
	if (mask.data == 0)
	{
		for(int i=0;i < height * width*_channel;i++)
			image.data[i] = data[i];
	}
	else
	{
		for(int i=0;i < height* width;i++)
		{
			if(mask.data[i]!=0)
			{
				for(int j=0;j<_channel;j++)
					image.data[i*_channel+j] = data[i*_channel+j];
			}
		}
	}
	if(dst.data!=0)
		dst.Release();	
	dst = image;
}

void Image::Convert(Image &dst, Change_Type type) const
{
	Image img;

	if(type == Default)
		switch(channel)
		{
			case C1:
				type = GRAY2RGB;
				break;
			case C3:
				type = RGB2GRAY;
				break;
			case C2:
			default:
				break;
		}
	switch(type)
	{
		case RGB2GRAY:
			img = Image(width, height, 1);
			for(int i=0;i < height * width;i++)
				img.data[i] = (data[i*3] + data[i*3+1] + data[i*3+2])/3;
			break;
		case GRAY2RGB:
			img= Image(width, height, 3);
			for(int i=0;i < height * width;i++)
				img.data[i*3] = img.data[i*3+1] = img.data[i*3+2] = data[i];
			break;		
		case RGB2BGR:
			img= Image(width, height, 3);
			for(int i=0;i < height * width;i++)
			{
				img.data[i*3] = data[i*3+2];
				img.data[i*3+2] = data[i*3];
			}
			break;
		default:
			break;
	}
	if(dst.data!=0)
		dst.Release();
	dst = img;
}

void Image::Draw(I_Point point, int size)
{
	Draw(point, size, point.color);
}

void Image::Draw(I_Point point, int size, int color[])
{
	size--;
	if(channel!=C3)
	{
		Log("Cannot Draw the Point",L_ERROR);
		return;
	}
	int x = point.x;
	int y = point.y;
	if( x<0 || y<0 || x >width-1 ||y>height-1)
	{
		Log("Cannot Draw the Point",L_ERROR);
		return;
	}
	
	for (int yy = y - size ; yy <= y + size ; yy++)
	for (int xx = x - size ; xx <= x + size ; xx++)  
		if (xx >= 0 && yy >= 0 && xx < width && yy < height)
		{
			data[yy* widthStep + xx*3] = color[0];
			data[yy* widthStep + xx*3 + 1] = color[1];
			data[yy* widthStep + xx*3 + 2] = color[2];
		}
}

void Image::Draw(I_Point point1, I_Point point2)
{
	if(channel!=C3)
	{
		Log("Cannot Draw the Line",L_ERROR);
		return;
	}

	int yy;
	int dy = point2.y-point1.y, dx = point2.x-point1.x, d=0;
	int o_y, opt;
	
	if (dy==0)
		o_y = 1;
	else
		o_y = dy/std::abs(dy);
	if (dx==0)
		opt = 1;
	else
		opt = dx/std::abs(dx);	
	
	if(abs(dx)>=abs(dy))
	{	
		yy = point1.y;
		for(int i = point1.x;;i=i+opt)
		{
			if(opt==1)
			{
				if(i>point2.x)
					break;
			}
			else
			{
				if(i<point2.x)
					break;
			}
			

			if(d<0)
				d += 2*abs(dy);
			else
				d += 2*(abs(dy)-abs(dx));
			yy += d>=0?o_y:0;		
			
			Draw(I_Point(i,yy));
		}
	}
	else
	{
		yy = point1.x;
		for(int i = point1.y;;i=i+o_y)
		{
			if(o_y==1)
			{
				if(i>point2.y)
					break;
			}
			else
			{
				if(i<point2.y)
					break;
			}
			

			if(d<0)
				d += 2*abs(dx);
			else
				d += 2*(abs(dx)-abs(dy));
			yy += d>=0?opt:0;
			
			Draw(I_Point(yy,i));
		}
	}
}

void Image::Draw(I_Rect box)
{
	if(channel!=C3)
	{
		Log("Cannot Draw the Box",L_ERROR);
		return;
	}
	
	for(int x = box.x - box.width/2; x <box.x + box.width/2; x++)
	{
		int y = box.y - box.height/2;
		if (x >= 0 && y >= 0 && x < width && y < height)
		{
			Draw(I_Point(x,y),1,box.color);
		}
		y = box.y + box.height/2 - 1;
		if (x >= 0 && y >= 0 && x < width && y < height)
		{
			Draw(I_Point(x,y),1,box.color);
		}		
	}
	
	for(int y = box.y - box.height/2; y <box.y + box.height/2; y++)
	{
		int x = box.x- box.width/2;
		if (x >= 0 && y >= 0 && x < width && y < height)
		{
			Draw(I_Point(x,y),1,box.color);
		}
		x = box.x + box.width/2 - 1;
		if (x >= 0 && y >= 0 && x < width && y < height)
		{
			Draw(I_Point(x,y),1,box.color);
		}		
	}
	
	Draw(I_Point(box.x ,box.y), 2 , box.color);
}

I_Point::I_Point(int _x, int _y ):x(_x), y(_y)
{
	type = 0;
	color[0] = 255;
	color[1] = 255;
	color[2] = 0;
}

I_Rect::I_Rect(int _x, int _y, int _width, int _height):x(_x), y(_y), width(_width), height(_height)
{
	type = 0;
	color[0] = 0;
	color[1] = 255;
	color[2] = 0;
}

void swap(Image &img1, Image &img2)
{
	if(img1.width == img2.width && img1.height == img2.height && img1.channel == img2.channel)
	{
		unsigned char*temp = img1.data;
		img1.data = img2.data;
		img2.data = temp;
	}
}
